#include <assert.h>
#include <pthread.h>
#include <signal.h>

#include "base/simapi.h"

#define SIG_SUSPEND SIGPWR

#define SIG_COUNT 2

int sigByThread[MAX_NUM_CPUS];

void* run (void* unused) {
    while (TRUE) { 
        if (sigByThread[Sim_GetMyId()]>=SIG_COUNT) {
            break;
        }
    }

    return NULL;
}

void suspend_handler (int sig, siginfo_t *info, void *unused) {
    sigByThread[Sim_GetMyId()]++;
    return;
}

int noop (int i) { return i; }

void wait_alittle () {
    for (int i=0; i < 500; i++) {
        noop(i);
    }
}

void mainX (int argc, const char **argv, const char **envp) {
    struct sigaction act;
    act.sa_flags = SA_RESTART|SA_SIGINFO;
    sigfillset(&act.sa_mask);
    act.sa_sigaction = suspend_handler;
    sigaction(SIG_SUSPEND, &act, NULL);
   
    Sim_Print0("Starting...\n");
    pthread_t pth[Sim_GetNumCpus()];
    for (int i=1; i<Sim_GetNumCpus(); i++) {
        int result = pthread_create(&(pth[i]), NULL, run, NULL);
        assert(result == 0);
    }
    Sim_Print0("Finished creating threads...\n");
    wait_alittle();

    for (int i=1; i<Sim_GetNumCpus(); i++) {
        int result = pthread_kill(pth[i], SIG_SUSPEND);
        assert(result == 0);
    }
    Sim_Print0("Finished sending signal...\n");
    wait_alittle();

    for (int i=1; i<Sim_GetNumCpus(); i++) {
        int result = pthread_kill(pth[i], SIG_SUSPEND);
        assert(result == 0);
    }
    Sim_Print0("Finished sending 2nd signal...\n");

    Sim_Print0("Joining...\n");
    for (int i=1; i<Sim_GetNumCpus(); i++) {
        Sim_Print1("Joining %d...\n", pth[i]);
        int result = pthread_join(pth[i], NULL);
        assert(result == 0);
    }

    Sim_Print0("Checking...\n");
    for (int i=1; i<Sim_GetNumCpus(); i++) {
        if (sigByThread[i]==SIG_COUNT) {
            Sim_Print1("Signal is correctly received for cpu[%d]\n", i);
        }
        else {
            Sim_Print1("Signal is NOT correctly received for cpu[%d]\n", i);
            Sim_Print3("  expecting %d signals, but got [%d] at address %p\n", 
                       SIG_COUNT, 
                       sigByThread[i],
                       (int)&sigByThread[i]);
        }
    }
}
